package com.fakeco.fakeproduct;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * This is a test who's behavior can be externally controlled using a set of static methods.
 *    
 * It uses a property file because there's no way to get the instance of the class 
 * that JUnit creates and we can't use a static variable because JUnit 
 * uses a different classloader. 
 *
 * @author <a href="mailto:benrady@gmail.com"Ben Rady</a>
 *
 */
public class TestFakeProduct extends AbstractTestFakeProduct
{
  private FakeProduct fakeProduct = null;
  private static File file = new File("TestFakeProduct.data");

  public TestFakeProduct(String name)
  {
    super(name);
  }  
    
  protected void setUp() throws Exception
  {
    super.setUp();
    if (!file.exists())
    {
      setUpState();
      file.deleteOnExit();
    }
    fakeProduct = new FakeProduct();
    runTestMethod("setUp");
  }

  protected void tearDown() throws Exception
  {
    fakeProduct = null;
    super.tearDown();
    runTestMethod("tearDown");
  }

  public void testNumber1() throws Exception
  { 
    runTestMethod("testNumber1");
    Object o = new Runnable()
    {
      public void run()
      {
        throw new IllegalStateException("This is an inner class and should not be run");        
      }      
    };
  }
  
  public void testNumber2() throws Exception
  { 
    runTestMethod("testNumber2");    
  }
   
  public static void setTestSuccess(String testName, String failureMessage, boolean pass) throws FileNotFoundException, IOException
  {
    if (pass)
      writeProperty(testName, "");
    else
      writeProperty(testName, failureMessage);
  }
   
  public static int getCallCount(String methodName) throws FileNotFoundException, IOException
  {
    Properties prop = loadProperties();
    String callCount = prop.getProperty(methodName + CALLCOUNT_POSTFIX);
    if (callCount == null)
      return 0;
    return Integer.parseInt(callCount);
  }

  public static void setUpState() throws IOException
  {
    tearDownState();
    assertTrue("Could not re-initialize test state", file.createNewFile());
  }

  public static void tearDownState()
  {
    if (file.exists())
    {
      file.deleteOnExit();
      file.delete();
    }
  } 

  
  private void runTestMethod(String methodName) throws Exception
  {    
    int callcount = getCallCount(methodName);
    writeProperty(methodName + CALLCOUNT_POSTFIX, Integer.toString(callcount + 1));
    String message = readProperty(methodName);
    if (!(message == null) && !message.equals(""))
    {
      if (message.endsWith(EXCEPTION_POSTFIX))
      {
        String exception = message.replace(EXCEPTION_POSTFIX, "");
        Class clazz = Class.forName(exception);
        throw ((Exception) clazz.newInstance());
      }
      fail(message);
    }
  }
  
  private static Properties loadProperties() throws FileNotFoundException, IOException
  {    
    if (!file.exists())
    {
      throw new IllegalStateException("You must call setUpState before using other methods on this class");
    }
    Properties properties = new Properties();
    InputStream inputStream = new FileInputStream(file);
    try
    {
      properties.load(inputStream);
    }
    finally
    {
      inputStream.close();
    }
    return properties;
  }

  private String readProperty(String testName) throws FileNotFoundException, IOException
  {
    Properties prop = loadProperties();
    String message = prop.getProperty(testName);
    return message;
  }
  
  private static void writeProperty(String key, String value) throws FileNotFoundException, IOException
  {
    if (!file.exists())
    {
      throw new IllegalStateException("You must call clearState before using other methods on this class");
    }
    Properties properties = loadProperties();
    properties.setProperty(key, value);
    FileOutputStream fostream = new FileOutputStream(file);
    properties.store(fostream, "");
    fostream.close();    
  }

  public static void setTestError(String testName, Class exceptionClass) throws FileNotFoundException, IOException
  {   
    if (exceptionClass == null)
      writeProperty(testName, "");
    else
      writeProperty(testName, exceptionClass.getName() + EXCEPTION_POSTFIX);    
  }
}
